// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;

namespace SnapObjects.Data
{
    internal class DataIncludeSetter<TModel> : IDataIncludeSetter<TModel>
    {
        private readonly string _key;
        private readonly bool _isList;
        private readonly TModel _model;
        private readonly IEnumerable<TModel> _models;
        private readonly IModelAccessor<TModel> _accessor;
        private readonly IDataPacker _dataPacker;

        public DataIncludeSetter(
            string key,
            TModel model,
            IDataPacker packer)
             : this(key, packer, false)
        {
            _model = model;
        }

        public DataIncludeSetter(
            string key,
            IEnumerable<TModel> models,
            IDataPacker packer)
            : this(key, packer, true)
        {
            _models = models;
        }

        private DataIncludeSetter(
            string key,
            IDataPacker packer,
            bool isList)
        {
            _key = key;
            _isList = isList;
            _dataPacker = packer;

            var factory = ModelFactoryFactory.Create<TModel>();
            _accessor = factory.GetAccessor();
        }

        /// <summary>
        ///     Adds the data of an embedded property of TModel object to the IDataPacker object, and specifies a name for the data.
        /// </summary>
        /// <typeparam name="TKey">The type of the embedded property in the TModel object to be included.</typeparam>
        /// <param name="key">The name specified for the data.</param>
        /// <param name="property">A function used to specify the embedded property in the TModel object to be included.</param>
        /// <returns>Returns the current IDataIncludeSetter object.</returns>
        public IDataIncludeSetter<TModel> Include<TKey>(
            string key, Func<TModel, TKey> property)
        {
            var dataPacker = (DataPacker)_dataPacker;

            void AddValue(DataKey name, TModel model)
            {
                var formater = new NormalPackerFormater<TKey>(property(model));

                dataPacker.AddFormater(name, formater);
            }

            if (_isList)
            {
                var n = 0;

                foreach (var model in _models)
                {
                    var name = new DataKey(
                        new DataKey(_key, n++),
                        new DataKey(key));

                    AddValue(name, model);
                }
            }
            else
            {
                AddValue(new DataKey(_key, key), _model);
            }

            return this;
        }

        /// <summary>
        ///     Adds the data of all embedded properties of TModel object to the IDataPacker object.
        /// </summary>
        public void IncludeAll()
        {
            var dataPacker = (DataPacker)_dataPacker;
            var embeddeds = ((IPocoModelMeta)_accessor.ModelMeta).EmbeddedProperties;

            foreach (var embedded in embeddeds)
            {
                void AddValue(DataKey name, TModel model)
                {
                    var value = _accessor.GetValue(model, embedded.Name);

                    dataPacker.AddFormater(name, new NormalPackerFormater<object>(value));
                }

                if (_isList)
                {
                    var n = 0;

                    foreach (var model in _models)
                    {
                        var name = new DataKey(
                            new DataKey(_key, n++),
                            new DataKey(embedded.Name));

                        AddValue(name, model);
                    }
                }
                else
                {
                    AddValue(new DataKey(_key, embedded.Name), _model);
                }
            }
        }
    }
}
